package de.akabahn.ebd.control;

import java.util.Observer;

import android.graphics.Color;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.NumberPicker;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import de.akabahn.ebd.comm.dbd.DBDParameter;
import de.akabahn.ebd.control.common.Config;
import de.akabahn.ebd.dbd.DBDAdapter;
import de.akabahn.ebd.dbd.DBDAdapterFactory;
import de.akabahn.ebd.dbd.DBDSubscriptionListener;

/**
 * @author Peter Eimann
 */
public class EBD_DBDWeicheActivity extends EBD_BaseActivity implements
	OnClickListener, OnCheckedChangeListener, DBDSubscriptionListener {

	private final static int UNDEFINDED = 0;
	private final static int LEFT = 1;
	private final static int RIGHT = 2;
	
	private DBDAdapter dbdAdapter;

	private String subscriptionReq;
	private String subscriptionCurr;
	private int requestedPosition;
	private boolean testStarted;
	private boolean playSound;
	private ToneGenerator tone;
	private Thread testRunner;
	private Exception threadException;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.dbdweiche);
		tone = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
	}

	@Override
	protected void onStart() {
		super.onStart();

		try {
			initializeUser();
			initializeView();
			initializeDbd();
			initializeButtons();
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.GREEN);
		} catch (Exception e) {
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.RED);
			handleException(e);
		}

		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	}

	@Override
	protected void onStop() {
		super.onStop();

		((Switch)findViewById(R.id.dbdweiche_switch_test)).setChecked(false);
		stopTest();

		if (subscriptionReq != null) {
			dbdAdapter.unsubscribe(subscriptionReq);
		}
		if (subscriptionCurr != null) {
			dbdAdapter.unsubscribe(subscriptionCurr);
		}

		try {
			if (dbdAdapter.isStarted()) {
				dbdAdapter.stop();
			}
		} catch (Exception e) {
			handleException(e);
		}

		getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
			case R.id.dbdweiche_button_check:
				executeCheck();
				break;
			case R.id.dbdweiche_button_end:
				executeEnd();
				break;
			case R.id.dbdweiche_button_relocate:
				executeRelocate();
				break;
			case R.id.dbdweiche_button_sound:
				playSound = ((ToggleButton)findViewById(R.id.dbdweiche_button_sound)).isChecked();
				if (playSound) {
					tone.startTone(ToneGenerator.TONE_PROP_BEEP);
				}
				break;
		}
	}

	@Override
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		switch (buttonView.getId()) {
			case R.id.dbdweiche_switch_test:
				executeTest(isChecked);
				break;
		}
	}

	@Override
	public synchronized void onChange(final DBDParameter dbd) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				ddc.add("EBD_DBDWeicheActivity.onChange()", dbd.toString());
				if (dbd.getName().equals(subscriptionReq)) {
					requestedPosition = dbd.getValue();
					setRequestedSwitchPosition(requestedPosition);
				} else if (dbd.getName().equals(subscriptionCurr)) {
					setCurrentSwitchPosition(dbd.getValue());
					if (playSound && (dbd.getValue() == LEFT || dbd.getValue() == RIGHT)) {
						tone.startTone(ToneGenerator.TONE_PROP_BEEP2);
					}
				}
			}
		});
	}

	private void executeCheck() {
		String input = ((EditText)findViewById(R.id.dbdweiche_switch)).getText().toString();
		if (!input.matches("\\d{2}W\\d{1,3}")) {
			Toast.makeText(this, getResources().getString(R.string.dbdweiche_switchNumberErr),
				Toast.LENGTH_SHORT).show();
			return;
		}

		if (subscriptionReq != null) {
			dbdAdapter.unsubscribe(subscriptionReq);
		}
		if (subscriptionCurr != null) {
			dbdAdapter.unsubscribe(subscriptionCurr);
		}

		setRequestedSwitchPosition(-1);
		setCurrentSwitchPosition(-1);

		try {
			subscriptionReq = input + "S";
			subscriptionCurr = input + "I";
			dbdAdapter.subscribe(subscriptionReq, this);
			dbdAdapter.subscribe(subscriptionCurr, this);

			findViewById(R.id.dbdweiche_button_end).setEnabled(true);
			findViewById(R.id.dbdweiche_button_relocate).setEnabled(true);
			findViewById(R.id.dbdweiche_button_sound).setEnabled(true);
			findViewById(R.id.dbdweiche_switch_test).setEnabled(true);
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.GREEN);
		} catch (Exception e) {
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.RED);
			handleException(e);
		}
	}

	private void executeEnd() {
		if (subscriptionReq != null) {
			dbdAdapter.unsubscribe(subscriptionReq);
		}
		if (subscriptionCurr != null) {
			dbdAdapter.unsubscribe(subscriptionCurr);
		}

		((Switch)findViewById(R.id.dbdweiche_switch_test)).setChecked(false);
		findViewById(R.id.dbdweiche_button_end).setEnabled(false);
		findViewById(R.id.dbdweiche_button_relocate).setEnabled(false);
		findViewById(R.id.dbdweiche_button_sound).setEnabled(false);
		findViewById(R.id.dbdweiche_switch_test).setEnabled(false);

		setRequestedSwitchPosition(-1);
		setCurrentSwitchPosition(-1);
		stopTest();
	}

	private void executeRelocate() {
		try {
			dbdAdapter.set(subscriptionReq, requestedPosition == LEFT ? RIGHT : LEFT);
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.GREEN);
		} catch (Exception e) {
			((TextView)findViewById(R.id.dbdweiche_headline)).setTextColor(Color.RED);
			handleException(e);
		}
	}

	private void executeTest(boolean isChecked) {
		if (isChecked) {
			startTest();
		} else {
			stopTest();
		}
	}
	
	private void initializeDbd() {
		boolean demoMode = preferences.getBoolean("pref_key_demo_mode_flag", false);
		String dbdServer = preferences.getString("pref_key_dbd_server", "");
		int dbdPort = Integer.parseInt(preferences.getString("pref_key_dbd_port", ""));

		if (demoMode) {
			dbdAdapter = DBDAdapterFactory.getSimulation();
		} else {
			dbdAdapter = DBDAdapterFactory.getImplementation(dbdServer, dbdPort, (Observer[])null);
		}

		if (demoMode) {
			Toast.makeText(this, getResources().getString(R.string.control_demoMode), Toast.LENGTH_SHORT).show();
		}

		dbdAdapter.start();
	}

	private void initializeUser() {
		TextView userInfo = (TextView)findViewById(R.id.dbdweiche_userInfo);
		userInfo.setText(getResources().getString(R.string.userInfo, userName, role));
	}

	private void initializeButtons() {
		EditText weiche = (EditText)findViewById(R.id.dbdweiche_switch);
		weiche.addTextChangedListener(new WeichenNummerTextWatcher());

		NumberPicker np = (NumberPicker)findViewById(R.id.dbdweiche_np_delay);
		np.setMinValue(1);
		np.setMaxValue(Config.TEST_MAX_DELAY);
		np.setWrapSelectorWheel(false);
		np.setValue(Config.TEST_DEF_DELAY);
		np.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);

		findViewById(R.id.dbdweiche_button_check).setOnClickListener(this);
		findViewById(R.id.dbdweiche_button_end).setOnClickListener(this);
		findViewById(R.id.dbdweiche_button_relocate).setOnClickListener(this);
		findViewById(R.id.dbdweiche_button_sound).setOnClickListener(this);
		((Switch)findViewById(R.id.dbdweiche_switch_test)).setOnCheckedChangeListener(this);
	}

	private void initializeView() {
		findViewById(R.id.dbdweiche_button_check).setEnabled(true);
		findViewById(R.id.dbdweiche_button_end).setEnabled(false);
		findViewById(R.id.dbdweiche_button_relocate).setEnabled(false);
		findViewById(R.id.dbdweiche_button_sound).setEnabled(false);
		findViewById(R.id.dbdweiche_switch_test).setEnabled(false);

		setRequestedSwitchPosition(-1);
		setCurrentSwitchPosition(-1);
	}

	private void startTest() {
		if (testRunner != null && testRunner.isAlive()) {
			return;
		}
		testRunner = new Thread(new TestRunner(requestedPosition));
		testRunner.start();
		testStarted = true;
		findViewById(R.id.dbdweiche_button_relocate).setEnabled(false);
	}

	private void stopTest() {
		if (testStarted) {
			testStarted = false;
			if (testRunner != null && testRunner.isAlive()) {
				testRunner.interrupt();
			}
			findViewById(R.id.dbdweiche_button_relocate).setEnabled(true);
			if (threadException != null) {
				handleException(threadException);
				threadException = null;
			}
		}
	}

	private void setRequestedSwitchPosition(int position) {
		setSwitchPosition(position, (TextView)findViewById(R.id.dbdweiche_text_reqvalue_val));
	}

	private void setCurrentSwitchPosition(int position) {
		setSwitchPosition(position, (TextView)findViewById(R.id.dbdweiche_text_currvalue_val));
	}

	private void setSwitchPosition(int position, TextView view) {
		if (position == UNDEFINDED) {
			view.setTextColor(Color.RED);
			view.setText("X");
		} else if (position == LEFT) {
			view.setTextColor(Color.GREEN);
			view.setText("<");
		} else if (position == RIGHT) {
			view.setTextColor(Color.GREEN);
			view.setText(">");
		} else {
			view.setText(" ");
		}
	}

	private class WeichenNummerTextWatcher implements TextWatcher {
		private boolean appendMode;
		
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before, int count) {
		}

		@Override
		public void afterTextChanged(Editable s) {
			if (s.length() == 1) {
				appendMode = true;
			} else if (s.length() == 2 && appendMode) {
				appendMode = false;
				EditText weiche = (EditText)findViewById(R.id.dbdweiche_switch);
				weiche.setText(weiche.getText().toString() + "W");
				weiche.setSelection(3);
			}
		}
	}

	private class TestRunner implements Runnable {

		private int position;

		public TestRunner(int initialPosition) {
			this.position = initialPosition;
		}

		@Override
		public void run() {
			try {
				do {
					position = position == LEFT ? RIGHT : LEFT;
					dbdAdapter.set(subscriptionReq, position);
					tone.startTone(ToneGenerator.TONE_PROP_BEEP);
					try {
						Thread.sleep(((NumberPicker)findViewById(R.id.dbdweiche_np_delay)).getValue() * 1000);
					} catch (InterruptedException e) {
					}
				} while (testStarted);
			} catch (Exception e) {
				threadException = e;
			} finally {
				tone.startTone(ToneGenerator.TONE_PROP_NACK);
			}
		}
	}
}
